﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using XieCan.AspMVC.Entities;
using XieCan.AspMVC.Extensions;
using XieCan.AspMVC.Helpers;
using XieCan.AspMVC.IService;
using XieCan.AspMVC.Models;

namespace XieCan.AspMVC.Service
{
    /// <summary>
    /// 公共的数据操作基类，封装了增删改查的基本操作
    /// </summary>
    /// <remarks>其他类只需要继承此类，便可拥有基本的增删改查的能力</remarks>
    /// <typeparam name="TModel">BaseModel及其所有子类类型</typeparam>
    public class BaseService<TModel> : XieCanBase, IBaseService<TModel>
        where TModel : BaseModel
    {
        /// <summary>
        /// 获取线程内唯一数据上下文
        /// </summary>
        protected EntitiesContext Entities => CallContextHelper.Single<EntitiesContext>();

        protected DbContextTransaction Transaction = null;

        /// <summary>
        /// 当前接口对象
        /// </summary>
        private IBaseService<TModel> IBaseService => this;

        private void Commit()
        {
            if (Transaction != null)
            {
                Transaction.Commit();
                Transaction.Dispose();
                Transaction = null;
            }
        }

        private void Rollback()
        {
            if (Transaction != null)
            {
                Transaction.Rollback();
                Transaction.Dispose();
                Transaction = null;
            }
        }

        private OperateResult<T> Catch<T>(Exception exception)
        {
            var dept = exception.DepthException();

            if (dept is DbEntityValidationException dbEntityValidationException)
            {
                StringBuilder stringBuilder = new StringBuilder();
                foreach (DbEntityValidationResult item in dbEntityValidationException.EntityValidationErrors)
                {
                    foreach (var error in item.ValidationErrors)
                    {
                        stringBuilder.AppendLine($"{error.PropertyName} => {error.ErrorMessage}");
                    }
                }
                return OperateResult<T>.Failed(stringBuilder.ToString());
            }
            else if (dept is DbUpdateException dbUpdateException)
            {
                dbUpdateException.Entries.Single().Reload();
                return OperateResult<T>.Failed(dbUpdateException);
            }
            else if (dept is SqlException sqlException)
            {
                switch (sqlException.Number)
                {
                    case 547:
                    //外键报错
                    //var fkError = ForeignKeyErrorFormatter(sqlException);
                    //break;
                    case 2627:
                    case 2601:
                        //唯一约束报错
                        Match match = Regex.Match(sqlException.Message, "Cannot insert duplicate key row in object '\\w+.\\w+' with unique index '\\w+'. The duplicate key value is \\(\\w+\\)");
                        if (match.Success)
                        {
                            Match value = Regex.Match(match.Value, "\\(\\w+\\)");
                            if (value.Success)
                            {
                                return OperateResult<T>.Failed($"\"{value.Value}\"已经被使用，请更换后重试！");
                            }
                            return OperateResult<T>.Failed("违反了唯一性约束，请勿插入重复值！");
                        }
                        break;
                }
                return OperateResult<T>.Failed(sqlException);
            }
            else
            {
                return OperateResult<T>.Failed(dept);
            }
        }

        OperateResult<bool> IBaseService<TModel>.Insert(params TModel[] models)
        {
            if (models == null || models.Count() < 1)
                return OperateResult<bool>.Failed("添加的实体不能为null");

            try
            {
                foreach (var model in models)
                {
                    if (model != null)
                        Entities.Entry(model).State = EntityState.Added;
                }
                return OperateResult<bool>.Succeeded(true);
            }
            catch (Exception ex)
            {
                return Catch<bool>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.InsertChanges(params TModel[] models)
        {
            var tran = IBaseService.BeginTransaction(System.Data.IsolationLevel.Chaos);
            if (tran.Success)
            {
                var result = IBaseService.Insert(models);
                if (result.Success)
                {
                    return IBaseService.SaveChanges();
                }
                return OperateResult<bool>.Failed(result.Error);
            }
            return OperateResult<bool>.Failed(tran.Error);
        }

        OperateResult<bool> IBaseService<TModel>.Delete(params TModel[] models)
        {
            if (models == null || models.Count() < 1)
                return OperateResult<bool>.Failed("删除的实体不能为null");

            try
            {
                foreach (var model in models)
                {
                    var result = IBaseService.Find(model.Id);
                    if (result.Success)
                    {
                        var temp = result.Data;
                        temp.IsDeleted = true;
                        Entities.Set<TModel>().Attach(temp);
                        Entities.Entry(temp).State = EntityState.Modified;
                    }
                }
                return OperateResult<bool>.Succeeded(true);
            }
            catch (Exception ex)
            {
                return Catch<bool>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.DeleteChanges(params TModel[] models)
        {
            var tran = IBaseService.BeginTransaction(System.Data.IsolationLevel.Chaos);
            if (tran.Success)
            {
                var result = IBaseService.Delete(models);
                if (result.Success)
                {
                    return IBaseService.SaveChanges();
                }
                return OperateResult<bool>.Failed(result.Error);
            }
            return OperateResult<bool>.Failed(tran.Error);
        }

        OperateResult<bool> IBaseService<TModel>.Update(string properties, params TModel[] models)
        {
            if (models == null || models.Count() < 1)
                return OperateResult<bool>.Failed("更新的实体不能为null");

            try
            {
                foreach (var model in models)
                {
                    var entry = Entities.Entry(model);
                    var strs = properties.Split(new char[] { ',', '，' });
                    if (string.IsNullOrWhiteSpace(properties) || strs.Length < 1)
                    {
                        entry.State = EntityState.Modified;
                    }
                    else
                    {
                        entry.State = EntityState.Unchanged;
                        foreach (var property in strs)
                        {
                            if (entry.Property(property) != null)
                                entry.Property(property).IsModified = true;
                        }
                    }
                }
                return OperateResult<bool>.Succeeded(true);
            }
            catch (Exception ex)
            {
                return Catch<bool>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.UpdateChanges(string properties, params TModel[] models)
        {
            var tran = IBaseService.BeginTransaction(System.Data.IsolationLevel.Chaos);
            if (tran.Success)
            {
                var result = IBaseService.Update(properties, models);
                if (result.Success)
                {
                    return IBaseService.SaveChanges();
                }
                return OperateResult<bool>.Failed(result.Error);
            }
            return OperateResult<bool>.Failed(tran.Error);
        }

        OperateResult<IQueryable<TModel>> IBaseService<TModel>.Select()
        {
            try
            {
                var models = Entities.Set<TModel>().AsNoTracking();
                return OperateResult<IQueryable<TModel>>.Succeeded(models);
            }
            catch (Exception ex)
            {
                return Catch<IQueryable<TModel>>(ex);
            }
        }

        OperateResult<IQueryable<TModel>> IBaseService<TModel>.Select(Expression<Func<TModel, bool>> whereLambda)
        {
            var result = IBaseService.Select();
            if (result.Success)
            {
                try
                {
                    var list = result.Data.Where(whereLambda).AsNoTracking();
                    return OperateResult<IQueryable<TModel>>.Succeeded(list);
                }
                catch (Exception ex)
                {
                    return Catch<IQueryable<TModel>>(ex);
                }
            }
            return OperateResult<IQueryable<TModel>>.Failed(result.Error);
        }

        OperateResult<IQueryable<TModel>> IBaseService<TModel>.Select<TKey>(Expression<Func<TModel, bool>> whereLambda, Expression<Func<TModel, TKey>> orderBy, bool isAsc)
        {
            var result = IBaseService.Select(whereLambda);
            if (result.Success)
            {
                try
                {
                    var list = result.Data;
                    var query = list.OrderBy(orderBy);
                    if (!isAsc)
                    {
                        query = list.OrderByDescending(orderBy);
                    }
                    return OperateResult<IQueryable<TModel>>.Succeeded(query);
                }
                catch (Exception ex)
                {
                    return Catch<IQueryable<TModel>>(ex);
                }
            }
            return OperateResult<IQueryable<TModel>>.Failed(result.Error);
        }

        OperateResult<IQueryable<TModel>> IBaseService<TModel>.Select<TKey>(Expression<Func<TModel, bool>> whereLambda, Expression<Func<TModel, TKey>> orderBy, out long rowCount, out long pageCount, bool isAsc, long pageIndex, int pageSize)
        {
            var result = IBaseService.Select(whereLambda, orderBy, isAsc);
            if (result.Success)
            {
                var list = result.Data;

                //获取总记录数
                rowCount = list.Count();

                //边界值判断
                pageIndex = pageIndex < 1 ? 1 : pageIndex;
                pageSize = pageSize < 1 ? 12 : pageSize;

                //获取总页数
                pageCount = (long)Math.Ceiling(rowCount * 1.0 / pageSize);

                //计算跳过数量
                long skip = (pageIndex - 1) * pageSize;
                while (skip >= int.MaxValue)
                {
                    list = list.Skip(int.MaxValue);
                    skip -= int.MaxValue;
                }
                if (skip > 0)
                {
                    list = list.Skip((int)skip);
                }

                return OperateResult<IQueryable<TModel>>.Succeeded(list.Take(pageSize));
            }
            rowCount = 0;
            pageCount = 0;
            return OperateResult<IQueryable<TModel>>.Failed(result.Error);
        }

        async Task<OperateResult<TModel>> IBaseService<TModel>.FindAsync(Expression<Func<TModel, bool>> whereLambda)
        {
            try
            {
                var model = await Entities.Set<TModel>().FirstOrDefaultAsync(whereLambda);
                return OperateResult<TModel>.Succeeded(model);
            }
            catch (Exception ex)
            {
                return Catch<TModel>(ex);
            }
        }

        OperateResult<TModel> IBaseService<TModel>.Find(Expression<Func<TModel, bool>> whereLambda)
        {
            try
            {
                var model = Entities.Set<TModel>().FirstOrDefault(whereLambda);
                return OperateResult<TModel>.Succeeded(model);
            }
            catch (Exception ex)
            {
                return Catch<TModel>(ex);
            }
        }

        async Task<OperateResult<TModel>> IBaseService<TModel>.FindAsync(Guid guid)
        {
            try
            {
                var model = await Entities.Set<TModel>().FindAsync(guid);
                return OperateResult<TModel>.Succeeded(model);
            }
            catch (Exception ex)
            {
                return Catch<TModel>(ex);
            }
        }

        OperateResult<TModel> IBaseService<TModel>.Find(Guid guid)
        {
            try
            {
                var model = Entities.Set<TModel>().Find(guid);
                return OperateResult<TModel>.Succeeded(model);
            }
            catch (Exception ex)
            {
                return Catch<TModel>(ex);
            }
        }


        OperateResult<bool> IBaseService<TModel>.BeginTransaction(System.Data.IsolationLevel level)
        {
            try
            {
                Transaction = Entities.Database.BeginTransaction(level);
                return OperateResult<bool>.Succeeded(true);
            }
            catch (Exception ex)
            {
                return Catch<bool>(ex);
            }
        }

        OperateResult<IEnumerable<T>> IBaseService<TModel>.ExecQuery<T>(string sql, params SqlParameter[] parameters)
        {
            try
            {
                IEnumerable<T> list = Entities.Database.SqlQuery<T>(sql, parameters);
                return OperateResult<IEnumerable<T>>.Succeeded(list);
            }
            catch (Exception ex)
            {
                return OperateResult<IEnumerable<T>>.Failed(ex);
            }
        }

        OperateResult<int> IBaseService<TModel>.ExecNonQuery(string sql, params SqlParameter[] parameters)
        {
            try
            {
                var num = Entities.Database.ExecuteSqlCommand(sql, parameters);
                return OperateResult<int>.Succeeded(num);
            }
            catch (Exception ex)
            {
                return Catch<int>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.SaveChanges()
        {
            try
            {
                var num = Entities.SaveChanges();
                Commit();
                return OperateResult<bool>.Succeeded(num > 0);
            }
            catch (Exception ex)
            {
                Rollback();
                return Catch<bool>(ex);
            }
        }

        async Task<OperateResult<bool>> IBaseService<TModel>.SaveChangesAsync()
        {
            try
            {
                var num = await Entities.SaveChangesAsync();
                Commit();
                return OperateResult<bool>.Succeeded(num > 0);
            }
            catch (Exception ex)
            {
                Rollback();
                return Catch<bool>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.Delete(params string[] ids)
        {
            if (ids == null || ids.Count() < 1)
                return OperateResult<bool>.Failed("删除的实体不能为null");

            try
            {
                var result = IBaseService.Select(p => ids.Contains(p.Id.ToString()));
                if (result.Success)
                {
                    var models = result.Data.ToArray();
                    IBaseService.Delete(models);
                }
                return OperateResult<bool>.Succeeded(true);
            }
            catch (Exception ex)
            {
                return Catch<bool>(ex);
            }
        }

        OperateResult<bool> IBaseService<TModel>.DeleteChanges(params string[] ids)
        {
            var tran = IBaseService.BeginTransaction(System.Data.IsolationLevel.Chaos);
            if (tran.Success)
            {
                var result = IBaseService.Delete(ids);
                if (result.Success)
                {
                    return IBaseService.SaveChanges();
                }
                return OperateResult<bool>.Failed(result.Error);
            }
            return OperateResult<bool>.Failed(tran.Error);
        }

        OperateResult<TModel> IBaseService<TModel>.Find(string guid)
        {
            return IBaseService.Find(new Guid(guid));
        }
    }
}